package com.laza.cloud.auth;

import com.laza.cloud.auth.service.security.MongoUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;


//@EnableResourceServer 注解自动增加了一个类型为 OAuth2AuthenticationProcessingFilter 的过滤器链，

@SpringBootApplication
@EnableResourceServer
@EnableDiscoveryClient
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthApplication {
	public static void main(String[] args) {
		SpringApplication.run(AuthApplication.class, args);
	}

	@Configuration
	@EnableWebSecurity
	//@EnableWebSecurity注解以及WebSecurityConfigurerAdapter一起配合提供基于web的security
	protected static class webSecurityConfig extends WebSecurityConfigurerAdapter {

		@Autowired
		private MongoUserDetailsService userDetailsService;

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			// @formatter:off
			http
				.authorizeRequests().anyRequest().authenticated()
			.and()
				//spring默认是开启 csrf防范的（如果使用命名空间需要开发者显示开启）， 如果想关闭，可以使用disable()方法
				.csrf().disable();
			// @formatter:on
		}

		//(使用 @Override 注解方法)会导致 AuthenticationManagerBuilder
		// 构建的是一个"局部的(local)“的AuthenticationManager，他会是全局
		// AuthenticationManager 的一个孩子。在SpringBoot应用中，你可以通
		// 过 @Autowired 把全局的 AuthenticationManager 注入到另外一个Bean中，
		// 但是除非你主动暴露否则是不能把本地的(local)的AuthenticationManager通过
		// 类似的方式注入到另一个Bean中的
		//它对于创建基于内存的(in-memory)、JDBC或者LDAP用户详情(user details)
		// 或者添加自定义的 UserDetailsService 支持的都非常好


		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
			auth.userDetailsService(userDetailsService)
					.passwordEncoder(new BCryptPasswordEncoder());
		}

		//加上这个，避免很多问题
		@Override
		@Bean
		public AuthenticationManager authenticationManagerBean() throws Exception {
			return super.authenticationManagerBean();
		}
	}

	//@EnableAuthorizationServer 注释用于配置 OAuth 2.0 授权服务器机制，加上任意一个@Beans
	// 去实现 AuthorizationServerConfigurer (这是一个hander适配器实现的空方法)。以下功能委托
	// 给由 spring 创造的独立 configurers 而且传递到 AuthorizationServerConfigurer：





	//server的多个属性可以通过自定义AuthorizationServerConfigurer类型
	// (如AuthorizationServerConfigurerAdapter的扩展)的Bean来定制

	@Configuration
	@EnableAuthorizationServer
	protected static class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {

		//这里配置的配置可以改成 jdbc redis 等其他方式
		private TokenStore tokenStore = new InMemoryTokenStore();

		//@Bean
		//public InMemoryTokenStore tokenStore() {
		//	return new InMemoryTokenStore();
		//}

		@Autowired
		@Qualifier("authenticationManagerBean")
		private AuthenticationManager authenticationManager;

		@Autowired
		private MongoUserDetailsService userDetailsService;

		@Autowired
		private Environment env;

		//ClientDetailsServiceConfigurer:这个configurer定义了客户端细节服务。客户详细信息可以被
		// 初始化,或者你可以参考现有的商店。
		////可以改成JDBC从库里读或者其他方式。
		@Override
		public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

			// TODO persist clients details


			// @formatter:off
			clients.inMemory()
					.withClient("browser")
					.authorizedGrantTypes("refresh_token", "password")
					.scopes("ui")
			.and()
					.withClient("account-service")
					.secret(env.getProperty("ACCOUNT_SERVICE_PASSWORD"))
					.authorizedGrantTypes("client_credentials", "refresh_token")
					.scopes("server")
			.and()
					.withClient("statistics-service")
					.secret(env.getProperty("STATISTICS_SERVICE_PASSWORD"))
					.authorizedGrantTypes("client_credentials", "refresh_token")
					.scopes("server")

			.and()
					.withClient("notification-service")
					.secret(env.getProperty("NOTIFICATION_SERVICE_PASSWORD"))
					.authorizedGrantTypes("client_credentials", "refresh_token")
					.scopes("server");
			// @formatter:on
		}

		//AuthorizationServerEndpointsConfigurer:定义了授权和令牌端点和令牌服务
		@Override
		public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
			endpoints
					.tokenStore(tokenStore)
					.authenticationManager(authenticationManager)
					.userDetailsService(userDetailsService);
		}

		//AuthorizationServerSecurityConfigurer:在令牌端点上定义了安全约束。
		@Override
		public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
			oauthServer
					.tokenKeyAccess("permitAll()")
					.checkTokenAccess("isAuthenticated()");
		}
	}
}
